// 实现这个项目的构建任务
// 在开发的时候，把构建的文件放在临时文件夹，可以避免每次构建都压缩文件输出
const {
  src,
  dest,
  series,
  parallel,
  watch
} = require('gulp')

const del = require('del')

// 热更新插件
const browserSync = require('browser-sync')
// 自动创建一个开发服务器
const bs = browserSync.create()

// gulp插件统一加载的插件
// 使用方法：plugins.插件名称
// 如果插件名有多个-横杠，那么插件名就可以使用驼峰命名法去使用
// 如：gulp-clean-css => plugins.cleanCss
const loadPlugins = require('gulp-load-plugins')


const plugins = loadPlugins()

// 预设数据，用于填充页面模板的数据
// 预设数据可以单独写一个json文件，然后载入到gulpfile.js中引用
const data = {
  menus: [{
      name: 'Home',
      icon: 'aperture',
      link: 'index.html'
    },
    {
      name: 'Features',
      link: 'features.html'
    },
    {
      name: 'About',
      link: 'about.html'
    },
    {
      name: 'Contact',
      link: '#',
      children: [{
          name: 'Twitter',
          link: 'https://twitter.com/w_zce'
        },
        {
          name: 'About',
          link: 'https://weibo.com/zceme'
        },
        {
          name: 'divider'
        },
        {
          name: 'About',
          link: 'https://github.com/zce'
        }
      ]
    }
  ],
  pkg: require('./package.json'),
  date: new Date()
}

/**
 * 文件清除任务
 */
const clean = () => {
  return del(['dist', 'temp'])
}

/**
 * css编译任务
 */
const style = () => {
  // src函数里，第二个参数可以定义构建后的基准目录路径
  // gulp的插件提供的基本上都是一个函数，返回的是文件的转换流
  // 一般_下划线开头的文件，被默认为依赖文件，并不会转换成css
  return src('src/assets/styles/*.scss', {
      base: 'src'
    })
    // scss转换css
    // 转换后的css文件，格式上可能不太美观
    // 在sass函数上，添加参数outputStyle:'expanded'意思是，转换后完全展开
    .pipe(plugins.sass({
      outputStyle: 'expanded'
    }))
    .pipe(dest('temp'))
    .pipe(bs.reload({
      stream: true
    }))
}

/**
 * 脚本的编译任务
 */
const script = () => {
  return src('src/assets/scripts/*.js', {
      base: 'src'
    })
    // babel转换需要安装@babel/core @babel/preset
    // babel只是一个转换平台，具体的转换是babel里的插件，
    // 而preset是一个插件的集合，所以babel函数里需要增加presets参数，
    // 使用preset-env 去转换ES6以上的代码 
    .pipe(plugins.babel({
      presets: ['@babel/preset-env']
    }))
    .pipe(dest('temp'))
    // 自动编译配置，在设置了热更新以后，可以加上bs.reload()函数
    // 当文件发生改变时，可以直接使用流的方式，把文件传送到浏览器
    .pipe(bs.reload({
      stream: true
    }))
}

/**
 * 页面编译任务 
 */
const page = () => {
  // 一般情况下，所有的html都是放在src根目录下
  return src('src/*.html', {
      base: 'src'
    })
    // 在swig()函数中，填入页面模板的数据
    .pipe(plugins.swig({
      data
    }))
    .pipe(dest('temp'))
    .pipe(bs.reload({
      stream: true
    }))
}

// 图片转换任务
const image = () => {
  return src('src/assets/images/**', {
    base: 'src'
  }).pipe(plugins.imagemin()).pipe(dest('dist'))
}

// 字体文件转换任务
const font = () => {
  return src('src/assets/fonts/**', {
    base: 'src'
  }).pipe(plugins.imagemin()).pipe(dest('dist'))
}

/**
 * 额外任务
 */
const extra = () => {
  return src('public/**', {
    base: 'public'
  }).pipe(dest('dist'))
}

/**
 * 构建html引用注释的解析
 * 把存在引用注释的文件(js文件/css文件)，写入到dist目录下
 * 可以在生产环境下引用
 */
const useref = () => {
  return src('temp/*.html', {
      base: 'temp'
    })
    // useref里面参数searchPath 查找拥有引用注释的文件路径
    .pipe(plugins.useref({
      searchPath: ['temp', '.']
    }))
    // 使用了 useref 以后，项目中可能会多了一些额外的js或者css的文件
    // 需要对这些文件进行压缩
    // if()函数是去判断文件的类型
    .pipe(plugins.if(/\.js$/, plugins.uglify())) // 压缩js
    .pipe(plugins.if(/\.css$/, plugins.cleanCss())) // 压缩css
    // htmlmin 默认只会压缩空白字符，并不会去压缩换行符或其他格式变化
    // 需要增加选项
    // collapseWhtiespace: true 压缩换行符或其他空白标签
    // minifyCSS: true 压缩css的换行符和空格
    // minifyJS: true 压缩js的换行符和空格
    .pipe(plugins.if(/\.html$/, plugins.htmlmin({
      collapseWhitespace: true,
      minifyCSS: true,
      minifyJS: true
    })))
    // 因为这种链式调用，会导致读写冲突，让部分的文件没有压缩成功
    // 那么换一个写出的路径，就可以解决读写冲突
    .pipe(dest('dist'))
}

/**
 * 开发服务器任务
 */
const serve = () => {
  // 源代码监听，watch([监听的路径]，[编译任务])
  watch('src/assets/styles/*.scss', style)
  watch('src/assets/scripts/*.js', script)
  watch('src/*.html', page)
  // watch('src/assets/images/**', image)
  // watch('src/assets/fonts/**', font)
  // watch('public/**', extra)

  // 监听图片和文字，当发生改变的时候不会重新构建项目
  // 只会单独刷新浏览器，让浏览器去向这些地址请求文件
  watch([
    'src/assets/images/**',
    'src/assets/fonts/**',
    'public/**'
  ], bs.reload)

  bs.init({
    notify: false,
    port: 2080, // 端口设置
    open: false, // 是否自动打开页面
    // 监听的文件路径，当监听的文件发生变化，浏览器就会刷新加载
    // files: 'dist/**', 
    // 设置开发服务器运行路径
    server: {
      // 减少开发阶段热更新时的机器开销
      // 让图片和文字，去加载src目录下，而不是去加载dist下
      // 这样的话，在构建的时候就可以减少文字和图片的压缩开销
      baseDir: ['temp', 'src', 'public'],
      // 路由配置，修改html里的地址引用映射
      routes: {
        '/node_modules': 'node_modules'
      }
    }
  })
}


/**
 * 组合任务，可以使用parallel并行执行
 */
const compile = parallel(style, script, page)

// 上线前执行的任务，全局构建
// 因为 useref 任务是依赖 compile 编译后，才可以压缩，
// 所以 compile 任务应该和 useref 是串行执行
const build = series(clean, parallel(series(compile, useref), image, font, extra))

// 开发阶段执行的任务，忽略图片和文字的构建，只针对html,css,js 
const develop = series(compile, serve)


/**
 * 等价于
 * exports.default = () => {
 * 
 * }
 */
module.exports = {
  clean,
  build,
  develop
}
